"""
IR Builder - Constructs IR from Roslyn JSON payloads.

This module provides functionality to parse the JSON output from the Roslyn analyzer
and build the intermediate representation (IR) tree.
"""

import json
from pathlib import Path
from typing import Any

from orchestrator.ir.nodes import (
    ClassDeclaration,
    ConstructorDeclaration,
    FieldDeclaration,
    MethodDeclaration,
    Parameter,
    PropertyDeclaration,
)


class IRBuilder:
    """
    Builds IR from Roslyn analyzer JSON output.

    The Roslyn analyzer produces JSON with the following structure:
    {
      "fileName": "Calculator.cs",
      "filePath": "/path/to/Calculator.cs",
      "classes": [
        {
          "name": "Calculator",
          "namespace": "SimpleProject",
          "modifiers": ["public"],
          "baseTypes": [],
          "members": [...]
        }
      ]
    }
    """

    def __init__(self) -> None:
        """Initialize the IR builder"""
        self.errors: list[str] = []

    def build_from_json(self, json_data: str | dict[str, Any]) -> list[ClassDeclaration]:
        """
        Build IR from JSON string or dictionary.

        Args:
            json_data: JSON string or dictionary from Roslyn analyzer

        Returns:
            List of ClassDeclaration IR nodes

        Raises:
            ValueError: If JSON is invalid or malformed
        """
        self.errors = []

        # Parse JSON if string
        if isinstance(json_data, str):
            try:
                data = json.loads(json_data)
            except json.JSONDecodeError as e:
                raise ValueError(f"Invalid JSON: {e}") from e
        else:
            data = json_data

        # Extract classes
        classes_data = data.get("classes", [])
        if not classes_data:
            self.errors.append("No classes found in JSON data")
            return []

        # Build IR for each class
        ir_classes: list[ClassDeclaration] = []
        for class_data in classes_data:
            try:
                ir_class = self._build_class(class_data, data.get("filePath"))
                ir_classes.append(ir_class)
            except Exception as e:
                self.errors.append(f"Error building class {class_data.get('name', 'unknown')}: {e}")

        return ir_classes

    def build_from_file(self, file_path: str | Path) -> list[ClassDeclaration]:
        """
        Build IR from a JSON file.

        Args:
            file_path: Path to JSON file

        Returns:
            List of ClassDeclaration IR nodes

        Raises:
            FileNotFoundError: If file doesn't exist
            ValueError: If JSON is invalid
        """
        path = Path(file_path)
        if not path.exists():
            raise FileNotFoundError(f"File not found: {file_path}")

        json_data = path.read_text(encoding="utf-8")
        return self.build_from_json(json_data)

    def _build_class(self, class_data: dict[str, Any], source_file: str | None) -> ClassDeclaration:
        """Build a ClassDeclaration IR node from JSON data"""
        # Extract members by type
        members = class_data.get("members", [])
        fields: list[FieldDeclaration] = []
        properties: list[PropertyDeclaration] = []
        methods: list[MethodDeclaration] = []
        constructors: list[ConstructorDeclaration] = []

        for member in members:
            kind = member.get("kind", "")
            try:
                if kind == "Field":
                    fields.append(self._build_field(member))
                elif kind == "Property":
                    properties.append(self._build_property(member))
                elif kind == "Method":
                    methods.append(self._build_method(member))
                elif kind == "Constructor":
                    constructors.append(self._build_constructor(member))
                else:
                    self.errors.append(f"Unknown member kind: {kind}")
            except Exception as e:
                self.errors.append(f"Error building {kind} '{member.get('name', 'unknown')}': {e}")

        # Check for modifiers
        modifiers = class_data.get("modifiers", [])
        is_abstract = "abstract" in modifiers
        is_sealed = "sealed" in modifiers
        is_static = "static" in modifiers

        return ClassDeclaration(
            name=class_data.get("name", "Unknown"),
            namespace=class_data.get("namespace", ""),
            modifiers=modifiers,
            base_types=class_data.get("baseTypes", []),
            fields=fields,
            properties=properties,
            methods=methods,
            constructors=constructors,
            is_abstract=is_abstract,
            is_sealed=is_sealed,
            is_static=is_static,
            source_file=source_file,
        )

    def _build_field(self, member_data: dict[str, Any]) -> FieldDeclaration:
        """Build a FieldDeclaration IR node"""
        modifiers = member_data.get("modifiers", [])
        return FieldDeclaration(
            name=member_data.get("name", ""),
            field_type=member_data.get("returnType", ""),
            modifiers=modifiers,
            is_readonly="readonly" in modifiers,
            is_const="const" in modifiers,
        )

    def _build_property(self, member_data: dict[str, Any]) -> PropertyDeclaration:
        """Build a PropertyDeclaration IR node"""
        # C# auto-properties are the most common case
        # We'll detect them by the absence of explicit getter/setter bodies
        return PropertyDeclaration(
            name=member_data.get("name", ""),
            property_type=member_data.get("returnType", ""),
            modifiers=member_data.get("modifiers", []),
            has_getter=True,  # Default assumption
            has_setter=True,  # Default assumption
            is_auto_property=True,  # Most C# properties are auto-properties
        )

    def _build_method(self, member_data: dict[str, Any]) -> MethodDeclaration:
        """Build a MethodDeclaration IR node"""
        modifiers = member_data.get("modifiers", [])
        parameters = self._build_parameters(member_data.get("parameters", []))

        return MethodDeclaration(
            name=member_data.get("name", ""),
            return_type=member_data.get("returnType", "void"),
            parameters=parameters,
            modifiers=modifiers,
            is_abstract="abstract" in modifiers,
            is_virtual="virtual" in modifiers,
            is_override="override" in modifiers,
            is_static="static" in modifiers,
        )

    def _build_constructor(self, member_data: dict[str, Any]) -> ConstructorDeclaration:
        """Build a ConstructorDeclaration IR node"""
        parameters = self._build_parameters(member_data.get("parameters", []))

        return ConstructorDeclaration(
            name=member_data.get("name", ""),
            parameters=parameters,
            modifiers=member_data.get("modifiers", []),
        )

    def _build_parameters(self, params_data: list[str]) -> list[Parameter]:
        """
        Build Parameter objects from parameter strings.

        Roslyn gives us parameters as strings like "int a" or "string name".
        """
        parameters: list[Parameter] = []

        for param_str in params_data:
            parts = param_str.strip().split(None, 1)
            if len(parts) == 2:
                param_type, param_name = parts
                parameters.append(Parameter(name=param_name, param_type=param_type))
            else:
                self.errors.append(f"Malformed parameter: {param_str}")

        return parameters


def build_ir_from_roslyn_json(json_data: str | dict[str, Any]) -> list[ClassDeclaration]:
    """
    Convenience function to build IR from Roslyn JSON.

    Args:
        json_data: JSON string or dictionary from Roslyn analyzer

    Returns:
        List of ClassDeclaration IR nodes
    """
    builder = IRBuilder()
    return builder.build_from_json(json_data)
